package com.bittech.server;

import com.bittech.util.CommUtil;
import com.bittech.vo.GroupChatVO;

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author: yuisama
 * @Date: 2019-06-13 13:33
 * @Description:
 */
public class MultiThreadServer {
    // 缓存所有在线客户端实例
    private static Map<String, Socket> clientSockets = new ConcurrentHashMap<>();
    // 缓存所有群聊实例
    private static Map<String, List<String>> groups = new ConcurrentHashMap<>();

    private static class ExcuteClient implements Runnable {
        private Socket client;
        private Scanner in;
        private PrintStream out;

        public ExcuteClient(Socket client) {
            this.client = client;
            try {
                this.in = new Scanner(client.getInputStream());
                this.out = new PrintStream(client.getOutputStream(),true,"UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            while (true) {
                if (in.hasNextLine()) {
                    String command = in.nextLine();
                    // 注册客户流程
                    if (command.startsWith("name")) {
                        String clientName = command.split(":")[1];
                        // 将所有在线用户名单发给用户
                        out.println(clientSockets.keySet());
                        // 通知其他客户端上线信息
                        sendUpAndDownInfo("newReload:"+clientName);
                        // 将客户端保存到缓存中
                        clientSockets.put(clientName,client);
                        System.out.println(clientName+"已经上线");
                        System.out.println("当前共有"+clientSockets.size()+"人在线!");
                        for (Map.Entry<String,Socket> entry : clientSockets.entrySet()) {
                            System.out.println("用户名:"+entry.getKey()+"="+entry.getValue().getPort());
                        }
                    }
                    // 私聊流程
                    if (command.startsWith("P:")) {
                        //P:yang2dog_yang66-hey i am yang66
                        String privateClientName = command.split(":")[1].split("_")[0];
//                        String msg = command.split(":")[1].split("_")[1];
                        privateChat(privateClientName,command);
                    }
                    // 注册群流程
                    if (command.startsWith("R:")) {
                        System.out.println("收到注册群信息为:"+command);
                        String jsonStr = command.substring(2);
                        GroupChatVO groupChatVO = (GroupChatVO) CommUtil.json2Object(jsonStr,GroupChatVO.class);
                        groups.put(groupChatVO.getGroupName(),groupChatVO.getMembersName());
                        System.out.println(groups);
                    }
                    // 群聊流程
                    if (command.startsWith("G:")) {
                        // G:groupName_senderName-msg
                        System.out.println("收到群聊信息为:"+command);
                        String groupName = command.split(":")[1].split("_")[0];
                        String msg = command.split("_")[1];
                        groupChat(groupName,msg);
                    }
                }
            }
        }

        // 私聊
        private void privateChat(String clientName,String msg) {
            Socket client = clientSockets.get(clientName);
            PrintStream out = null;
            try {
                out = new PrintStream(client.getOutputStream(),
                        true,"UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
            out.println(msg);
        }

        private void groupChat(String groupName,String msg) {
            if (groups.get(groupName) == null) {
                System.err.println("没有此群聊信息!");
                return;
            }
            List<String> membersName = groups.get(groupName);
            GroupChatVO groupChatVO = new GroupChatVO();
            groupChatVO.setGroupName(groupName);
            groupChatVO.setMembersName(membersName);
            String jsonStr = CommUtil.object2Json(groupChatVO);
            for (String memberName : membersName) {
                Socket memberSocket = clientSockets.get(memberName);
                try {
                    PrintStream out =
                            new PrintStream(memberSocket.getOutputStream(),true,"UTF-8");
                    // msg为senderName-msg
                    String out2ClientStr =
                            "G:"+ jsonStr + "_"+ msg;
                    out.println(out2ClientStr);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


        // 发送上线、下线信息
        private void sendUpAndDownInfo(String msg) {
            // 取出当前Map中所有实体
            Set<Map.Entry<String,Socket>> entrySet = clientSockets.entrySet();
            for (Map.Entry<String,Socket> entry : entrySet) {
                try {
                    Socket client = entry.getValue();
                    PrintStream out =
                            new PrintStream(client.getOutputStream(),true,"UTF-8");
                    out.println(msg);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        ExecutorService service = Executors.newFixedThreadPool(50);
        Properties properties = CommUtil.loadProperties("socket.properties");
        int port = Integer.valueOf(properties.getProperty("port"));
        ServerSocket serverSocket = new ServerSocket(port);
        for (int i = 0;i < 50;i++) {
            System.out.println("等待客户端连接...");
            Socket client = serverSocket.accept();
            System.out.println("有新的客户端连接，端口号为:"+client.getPort());
            service.submit(new ExcuteClient(client));
        }
        serverSocket.close();
        service.shutdown();
    }
}
